<?php
require_once(dirname(__FILE__).'/../Abstract.php');
class AbstractORM extends AbstractModel 
{
	/*Constants*/
	const PATTERN_GET_BY='/getBy(.*)/';
	const PATTERN_GET_ALL_BY='/getAllBy(.*)/';
	const PATTERN_GET_ALL_LIKE='/getsAllLike(.*)/';
	const PATTERN_GET_ALL='/getAllRows/';
	
	const PATTERN_FETCH_BY='/fetchBy(.*)/';
	const PATTERN_FETCH_ALL_BY='/fetchAllBy(.*)/';
	const PATTERN_FETCH_ALL_LIKE='/fetchAllLike(.*)/';
	const PATTERN_FETCH_ALL='/fetchAllRows/';
	
	const PATTERN_REMOVE_BY='/removeBy(.*)/';
	const PATTERN_REMOVE_LIKE='/removeAllLike(.*)/';
	
	const PATTERN_COUNT_BY='/countBy(.*)/';
	const PATTERN_COUNT_LIKE='/countAllLike(.*)/';
	const PATTERN_COUNT_ALL='/countAll/';
	
	/*Protected Properties*/
	protected $_name = NULL;
	protected $_primary = 'id';
	protected $_order = NULL;
	protected $_limit = NULL;

	/*Construct*/
    public function __construct()
    {
        // Call the Model constructor
        parent::__construct();
    }
	
	/*Private Functions*/
	/**
	 * Transform the name of the fields from "ParentId" to "parent_id"
	 * @param array $keys
	 * @return array
	 */ 
	private static function normalizeKeys($keys){
		if (!is_array($keys))
			return strtolower(substr(preg_replace('/([A-Z]+)/', '_\1',$keys),1));
		foreach($keys as $k=>$v) $keys[$k] = strtolower(substr(preg_replace('/([A-Z]+)/', '_\1',$v),1));
		return $keys;
	}
	/**
	 * Get the name of the fields and separator from the name of the function
	 * @param string $name
	 * @return array
	 */
	private static function splitKeys($name){
		$where = array();
		preg_match_all('/(And|Or)[A-Z]/', $name, $where);
		$name = preg_replace('/(And|Or)([A-Z])/', '###\2', $name);
		$keys = explode('###', $name);
		return array(
			'keys'=>$keys,
			'where'=>$where
		);
	}
	/**
	 * Add Order By to the query
	 * @param Zend_Db_Select $sql
	 * @return Zend_Db_Select
	 */
	protected function _sqlOrder(){
        if ($this->_order !== NULL) 
			if (!is_array($this->_order)) $this->db->order_by($this->_order, "asc");
			else foreach ($this->_order as $order) $this->db->order_by($order);
		return $this->db;
	}
	/**
	 * Add Limit to the query
	 * @param Zend_Db_Select $sql
	 * @return Zend_Db_Select
	 */
	protected function _sqlLimit(){
		if ($this->_limit !== NULL)
			if (!is_array($this->_limit)) $this->db->limit($this->_limit);
			else $this->db->limit($this->_limit[0], $this->_limit[1]);
		return $this->db;
	}
	/**
	 * Add conditions to a get query
	 * @param Zend_Db_Select $sql
	 * @return Zend_Db_Select
	 */
	protected function _sqlAddGetWhere($fields, $where){
		foreach($fields as $field=>$value)
			if (is_array($value) && !empty($value))
				$this->db->where_in("`$field`", $value);
			elseif ($value===null) $this->db->where("`$field`", NULL);
			elseif (empty($value)) $this->db->where("1=0");
			else $this->db->where("`$field`", $value);
		return $this->db;
	}
	/**
	 * Add LIKE conditions to a get query
	 * @param Zend_Db_Select $sql
	 * @return Zend_Db_Select
	 */
	protected function _sqlAddGetWhereLike($fields, $where){
		foreach($fields as $field=>$value)
			if (is_array($value) && !empty($value))
                $this->db->where_in("`$field`", $value);
			elseif ($value===null) $this->db->where("`$field`", NULL);
			elseif (empty($value)) $this->db->where("1=0");
			else $this->db->like("`$field`", $value);
		return $this->db;
	}
	/**
	 * Add conditions to a fetch query
	 * @param Zend_Db_Select $sql
	 * @return Zend_Db_Select
	 */
	protected function _sqlAddFetchWhere($fields, $where){
		$name = get_class($this);
		foreach($fields as $field=>$value)
			if (is_array($value) && !empty($value))
				$this->db->where_in("`$name`.`$field`", $value);
			elseif ($value===null) $this->db->where("`$name`.`$field`", NULL);
			elseif (empty($value)) $this->db->where("1=0");
			else $this->db->where("`$name`.`$field`", $value);
		return $this->db;
	}
	/**
	 * Add LIKE conditions to a fetch query
	 * @param Zend_Db_Select $sql
	 * @return Zend_Db_Select
	 */
	protected function _sqlAddFetchWhereLike($fields, $where){
		$name = get_class($this);
		foreach($fields as $field=>$value)
			if (is_array($value) && !empty($value))
				$this->db->where_in("`$name`.`$field`", $value);
			elseif ($value===null) $this->db->where("`$name`.`$field`", NULL);
			elseif (empty($value)) $this->db->where("1=0");
			else $this->db->like("`$name`.`$field`", $value);
		return $this->db;
	}
	/**
	 * Join with all referenced tables 
	 * @param Zend_Db_Select $sql
	 * @return Zend_Db_Select
	 *//*
	protected function _sqlAddFetchJoin(){
		$name = get_class($this);
		$this->db->from("{$this->_name} $name");
		foreach($this->_referenceMap as $key=>$value){
			$_ref_class = $value['refTableClass'];
			$_ref_field = $value['refColumns'];
			$_field = $value['columns'];
			eval ('$table_name = '.$_ref_class.'::this()->_getTableName();');
			eval ('$columns = '.$_ref_class.'::this()->_getColumns();');
			$cols = array();
			
			foreach($columns as $col) $cols[$key.'__'.$col] = $col;
			$this->db->joinLeft(array($key=>$table_name), "`$name`.`$_field` = `$key`.`$_ref_field`", $cols);
		}
		return $this->db;
	}
	public function _getTableName(){return $this->_name;}
	public function _getPrimary(){return $this->_primary;}
	public function _getColumns(){return $this->_getCols();}
    */
	/**
	 * Insert or update a row
	 */
	public function save($record) {
	//var_dump($record, $this->_primary);
    	//if(!is_array($record)) $record = $record->toArray();
		$this->_primary = (is_array($this->_primary))?array_pop($this->_primary):$this->_primary;
    	if(isset($record[$this->_primary])){
            $id = $record[$this->_primary]; unset($record[$this->_primary]);
    	    if (is_array($id)&&!empty($id)) {
                $this->db->where_in("`{$this->_primary}`", $id);
                $this->db->update($this->_name, $record);
            } else {
                $this->db->where("`{$this->_primary}`", $id);
                $this->db->update($this->_name, $record);
        	}
            return $id;
    	}
        return $this->db->insert($this->_name, $record);
    }
	/**
	 * Execute an SQL query
	 */
	public function exec($sql){
		return $this->db->query($sql)->result();
	}
	/**
	 * Get a record by fields
	 * @param array $fields
	 * @param array $where
	 * @return Zend_Db_Table_Row
	 */
	protected function __getByFields($fields=array(), $where=null){
		$this->db->from($this->_name);
		$this->_sqlAddGetWhere($fields, $where);
		$this->_sqlOrder(); $this->_sqlLimit();
		return $this->db->get()->row();
	}
	/**
	 * Get all records by fields
	 * @param array $fields
	 * @param array $where
	 * @return Zend_Db_Table_Rowset
	 */
	protected function __getAllByFields($fields=array(), $where=null){
		$this->db->from($this->_name);
		$this->_sqlAddGetWhere($fields, $where);
		$this->_sqlOrder(); $this->_sqlLimit();
		return $this->db->get()->result();
	}
	/**
	 * Get all records by fields using LIKE
	 * @param array $fields
	 * @param array $where
	 * @return Zend_Db_Table_Rowset
	 */
	protected function __getAllLikeFields($fields=array(), $where=null){
		$this->db->from($this->_name);
		$this->_sqlAddGetWhereLike($fields, $where);
		$this->_sqlOrder(); $this->_sqlLimit();
		return $this->db->get()->result();
	}
	/**
	 * Count the number of records based on the given fields
	 * @param array $fields
	 * @param array $where
	 * @return int
	 */
	protected function __countByFields($fields=array(), $where=null){
		$this->db->from($this->_name);
		$this->_sqlAddGetWhere($fields, $where);
		return $db->count_all_results();
	}
	/**
	 * Count the number of records based on the given fields using LIKE
	 * @param array $fields
	 * @param array $where
	 * @return int
	 */
	protected function __countAllLikeFields($fields=array(), $where=null){
		$this->db->from($this->_name);
		$this->_sqlAddGetWhereLike($fields, $where);
		return $db->count_all_results();
	}
	/**
	 * Fetch a record by fields
	 * @param array $fields
	 * @param array $where
	 * @return Zend_Db_Table_Row
	 */
	protected function __fetchByFields($fields=array(), $where=null){
		$this->db->from($this->_name);
		$this->_sqlAddFetchWhere($fields,$where);
		$this->_sqlOrder(); $this->_sqlLimit();
		return $this->db->get()->row();
	}
	/**
	 * Fetch all records by fields
	 * @param array $fields
	 * @param array $where
	 * @return Zend_Db_Table_Rowset
	 */
	protected function __fetchAllByFields($fields=array(), $where=null){
		$this->db->from($this->_name);
		$this->_sqlAddFetchWhere($fields,$where);
		$this->_sqlOrder(); $this->_sqlLimit();
		return $this->db->get()->result();
	}
	/**
	 * Fetch all records by fields using LIKE
	 * @param array $fields
	 * @param array $where
	 * @return Zend_Db_Table_Rowset
	 */
	protected function __fetchAllLikeFields($fields=array(), $where=null){
		$this->db->from($this->_name);
		$this->_sqlAddFetchWhereLike($fields, $where);
		$this->_sqlOrder(); $this->_sqlLimit();
		return $this->db->get()->result();
	}
	/**
	 * Remove records by fields
	 * @param array $fields
	 * @param array $where
	 * @return Number
	 */
	protected function __removeByFields($fields=array(), $where=null){
		foreach($fields as $field=>$value){
			if (is_array($value) && empty($value)) return false;
			if (is_array($value))
                $this->db->where_in("`$field`", $value);
			elseif(is_string($value)) $this->db->where("`$field`", addslashes($value));
			elseif ($value===null) $this->db->where("`$field`", NULL);
			else $this->db->where("`$field`", addslashes($value));
		}
		return $this->delete($this->_name);
	}
	/**
	 * Remove all records using LIKE
	 * @param array $fields
	 * @param array $where
	 * @return Number
	 */
	protected function __removeLikeFields($fields=array(), $where=null){
		foreach($fields as $field=>$value){
			if (is_array($value) && empty($value)) return false;
			if (is_array($value))
				$this->db->where_in("`$field`", $value);
			elseif(is_string($value)) $this->db->like("`$field`", addslashes($value));
			elseif ($value===null) $this->db->where("`$field`", NULL);
			else $this->db->where("`$field`", addslashes($value));
		}
		return $this->delete($this->_name);
	}
	/**
	 * Parse the name of the called function and test if it matches the current patterns 
	 * and return the appropriate result.
	 * @param string $name
	 * @param array $arguments
	 * @return mixed
	 */
	public function __call($name, $arguments) {
        preg_match(self::PATTERN_GET_BY, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__getByFields'), array($args, $where));
		}
		preg_match(self::PATTERN_GET_ALL_BY, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__getAllByFields'), array($args, $where));
		}
		preg_match(self::PATTERN_GET_ALL_LIKE, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__getAllLikeFields'), array($args, $where));
		}
		preg_match(self::PATTERN_GET_ALL, $name, $matches);
		if (count($matches)){
			return call_user_func_array(array($this, '__getAllByFields'), array());
		}
		preg_match(self::PATTERN_FETCH_BY, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__fetchByFields'), array($args, $where));
		}
		preg_match(self::PATTERN_FETCH_ALL_BY, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__fetchAllByFields'), array($args, $where));
		}
		preg_match(self::PATTERN_FETCH_ALL_LIKE, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__fetchAllLikeFields'), array($args, $where));
		}
		preg_match(self::PATTERN_FETCH_ALL, $name, $matches);
		if (count($matches)){
			return call_user_func_array(array($this, '__fetchAllByFields'), array());
		}
		preg_match(self::PATTERN_REMOVE_BY, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__removeByFields'), array($args, $where));
		}
		preg_match(self::PATTERN_REMOVE_LIKE, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__removeLikeFields'), array($args, $where));
		}
		preg_match(self::PATTERN_COUNT_BY, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args=array();
			if (is_array($keys) && !empty($keys[0]) && is_array($arguments))
				$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__countByFields'), array($args, $where));
		}
		preg_match(self::PATTERN_COUNT_LIKE, $name, $matches);
		if (count($matches)){
			$keys = self::splitKeys($matches[1]); $where=$keys['where']; $keys=$keys['keys'];
			$keys = self::normalizeKeys($keys);
			$args=array();
			if (is_array($keys) && !empty($keys[0]) && is_array($arguments))
				$args = array_combine($keys, $arguments);
			return call_user_func_array(array($this, '__countAllLikeFields'), array($args, $where));
		}
		preg_match(self::PATTERN_COUNT_ALL, $name, $matches);
		if (count($matches)){
			return call_user_func_array(array($this, '__countByFields'), array());
		}
    }
}


